﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Text;

namespace EasyNet
{
    public class RSA
    {
        #region key
        /// <summary>
        /// 从xml中读取秘钥
        /// </summary>
        /// <param name="xml_"></param>
        /// <param name="is_private_key"></param>
        /// <param name="use_pkcs8"></param>
        /// <returns></returns>
        public static byte[] ReadFromXml(string xml_, bool is_private_key, bool use_pkcs8)
        {
            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.FromXmlString(xml_);
                if (is_private_key)
                {
                    return use_pkcs8 ? rsa.ExportPkcs8PrivateKey() : rsa.ExportRSAPrivateKey();
                }
                return use_pkcs8 ? rsa.ExportSubjectPublicKeyInfo() : rsa.ExportRSAPublicKey();
            }
        }
        /// <summary>
        /// 将秘钥保存到xml中
        /// </summary>
        /// <param name="buffer_"></param>
        /// <param name="is_private_key"></param>
        /// <param name="use_pkcs8"></param>
        /// <returns></returns>
        public static string WriteToXml(byte[] buffer_, bool is_private_key, bool use_pkcs8)
        {
            using (var rsa = CreateRSACryptoServiceProvider(buffer_, is_private_key, use_pkcs8))
            {
                return rsa.ToXmlString(is_private_key);
            }
        }

        /// <summary>
        /// 获取RSA非对称加密的Key
        /// </summary>
        /// <param name="public_key"></param>
        /// <param name="private_key"></param>
        public static void GenerateRsaKey(bool use_pkcs8, out byte[] public_key, out byte[] private_key)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.KeySize = 1024;//1024位
                if (use_pkcs8)
                {
                    //使用pkcs8填充方式导出
                    public_key = rsa.ExportSubjectPublicKeyInfo();//公钥
                    private_key = rsa.ExportPkcs8PrivateKey();//私钥
                }
                else
                {
                    //使用pkcs1填充方式导出
                    public_key = rsa.ExportRSAPublicKey();//公钥
                    private_key = rsa.ExportRSAPrivateKey();//私钥
                }
            }
        }
        /// <summary>
        /// 从Pfx文件获取RSA非对称加密的Key
        /// </summary>
        /// <param name="pfx_file_name"></param>
        /// <param name="public_key"></param>
        /// <param name="private_key"></param>
        public static void ReadFromPfx(string pfx_file_name, string password_, out byte[] public_key, out byte[] private_key)
        {
            X509Certificate2 x509Certificate2 = new X509Certificate2(pfx_file_name, password_, X509KeyStorageFlags.Exportable);
            public_key = x509Certificate2.GetRSAPublicKey().ExportRSAPublicKey();
            private_key = x509Certificate2.GetRSAPrivateKey().ExportRSAPrivateKey();
        }
        /// <summary>
        /// 从Crt文件中读取公钥
        /// </summary>
        /// <param name="crt_file_name"></param>
        /// <param name="password_"></param>
        /// <returns></returns>
        public static byte[] ReadPublicKeyFromCrt(string crt_file_name, string password_)
        {
            X509Certificate2 x509Certificate2 = new X509Certificate2(crt_file_name, password_, X509KeyStorageFlags.Exportable);
            var publicKey = x509Certificate2.GetRSAPublicKey().ExportRSAPublicKey();
            return publicKey;
        }
        #endregion

        #region Pkcs1 and Pkcs8

        /// <summary>
        /// Pkcs1转Pkcs8
        /// </summary>
        /// <param name="is_private_key"></param>
        /// <param name="buffer_"></param>
        /// <returns></returns>
        public static byte[] Pkcs1ToPkcs8(bool is_private_key, byte[] buffer_)
        {
            using (var rsa = new RSACryptoServiceProvider())
            {
                if (is_private_key)
                {
                    rsa.ImportRSAPrivateKey(buffer_, out _);
                    return rsa.ExportPkcs8PrivateKey();
                }
                else
                {
                    rsa.ImportRSAPublicKey(buffer_, out _);
                    return rsa.ExportSubjectPublicKeyInfo();
                }
            }
        }
        /// <summary>
        /// Pkcs8转Pkcs1
        /// </summary>
        /// <param name="is_private_key"></param>
        /// <param name="buffer_"></param>
        /// <returns></returns>
        public static byte[] Pkcs8ToPkcs1(bool is_private_key, byte[] buffer_)
        {
            using (var rsa = new RSACryptoServiceProvider())
            {
                if (is_private_key)
                {
                    rsa.ImportPkcs8PrivateKey(buffer_, out _);
                    return rsa.ExportRSAPrivateKey();
                }
                else
                {
                    rsa.ImportSubjectPublicKeyInfo(buffer_, out _);
                    return rsa.ExportRSAPublicKey();
                }
            }
        }

        #endregion

        #region RSA

        /// <summary>
        /// 获取一个RSACryptoServiceProvider
        /// </summary>
        /// <param name="is_private_key"></param>
        /// <param name="buffer_"></param>
        /// <param name="use_pkcs8"></param>
        /// <returns></returns>
        public static RSACryptoServiceProvider CreateRSACryptoServiceProvider(byte[] buffer_, bool is_private_key, bool use_pkcs8 = false)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            if (is_private_key)
            {
                if (use_pkcs8)
                    rsa.ImportPkcs8PrivateKey(buffer_, out _);
                else
                    rsa.ImportRSAPrivateKey(buffer_, out _);
            }
            else
            {
                if (use_pkcs8)
                    rsa.ImportSubjectPublicKeyInfo(buffer_, out _);
                else
                    rsa.ImportRSAPublicKey(buffer_, out _);
            }
            return rsa;
        }

        /// <summary>
        /// RSA公钥加密
        /// </summary>
        /// <param name="value_">待加密的明文</param>
        /// <param name="public_key">公钥</param>
        /// <param name="use_pkcs8">是否使用pkcs8填充</param>
        /// <returns></returns>
        public static string RSAEncrypt(string value_, byte[] public_key, bool use_pkcs8 = false)
        {
            if (string.IsNullOrEmpty(value_)) return value_;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(public_key, false, use_pkcs8))
            {
                var buffer = Encoding.UTF8.GetBytes(value_);
                buffer = rsa.Encrypt(buffer, false);

                //使用hex格式输出数据
                StringBuilder result = new StringBuilder();
                foreach (byte b in buffer)
                {
                    result.AppendFormat("{0:x2}", b);
                }
                return result.ToString();
                //或者使用下面的输出
                //return BitConverter.ToString(buffer).Replace("-", "").ToLower();
            }
        }
        /// <summary>
        /// RSA私钥解密
        /// </summary>
        /// <param name="value_">密文</param>
        /// <param name="private_key">私钥</param>
        /// <param name="use_pkcs8">是否使用pkcs8填充</param>
        /// <returns></returns>
        public static string RSADecrypt(string value_, byte[] private_key, bool use_pkcs8 = false)
        {
            if (string.IsNullOrEmpty(value_)) return value_;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(private_key, true, use_pkcs8))
            {
                //转换hex格式数据为byte数组
                var buffer = new byte[value_.Length / 2];
                for (var i = 0; i < buffer.Length; i++)
                {
                    buffer[i] = (byte)Convert.ToInt32(value_.Substring(i * 2, 2), 16);
                }
                buffer = rsa.Decrypt(buffer, false);
                return Encoding.UTF8.GetString(buffer);
            }
        }
        /// <summary>
        /// RSA私钥生成签名
        /// </summary>
        /// <param name="value_">原始值</param>
        /// <param name="publicKey">公钥</param>
        /// <param name="halg_">签名hash算法：SHA，SHA1，MD5，SHA256，SHA384，SHA512</param>
        /// <param name="use_pkcs8">是否使用pkcs8填充</param>
        /// <returns></returns>
        public static string Sign(string value_, byte[] private_key, string halg_ = "MD5", bool use_pkcs8 = false)
        {
            if (string.IsNullOrEmpty(value_)) return value_;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(private_key, true, use_pkcs8))
            {
                byte[] buffer = Encoding.UTF8.GetBytes(value_);
                buffer = rsa.SignData(buffer, HashAlgorithm.Create(halg_));

                //使用hex格式输出数据
                StringBuilder result = new StringBuilder();
                foreach (byte b in buffer)
                {
                    result.AppendFormat("{0:x2}", b);
                }
                return result.ToString();
                //或者使用下面的输出
                //return BitConverter.ToString(buffer).Replace("-", "").ToLower();
            }
        }
        /// <summary>
        /// RSA公钥验证签名
        /// </summary>
        /// <param name="value_">原始值</param>
        /// <param name="public_key">公钥</param>
        /// <param name="signature_">签名</param>
        /// <param name="halg_">签名hash算法：SHA，SHA1，MD5，SHA256，SHA384，SHA512</param>
        /// <param name="use_pkcs8">是否使用pkcs8填充</param>
        /// <returns></returns>
        public static bool Verify(string value_, byte[] public_key, string signature_, string halg_ = "MD5", bool use_pkcs8 = false)
        {
            if (string.IsNullOrEmpty(value_)) return false;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(public_key, false, use_pkcs8))
            {
                //转换hex格式数据为byte数组
                var buffer = new byte[signature_.Length / 2];
                for (var i = 0; i < buffer.Length; i++)
                {
                    buffer[i] = (byte)Convert.ToInt32(signature_.Substring(i * 2, 2), 16);
                }
                return rsa.VerifyData(Encoding.UTF8.GetBytes(value_), HashAlgorithm.Create(halg_), buffer);
            }
        }
        #endregion
    }
}
